package com.atguigu.lease.web.app.service.impl;


import com.atguigu.lease.common.advice.LeaseException;
import com.atguigu.lease.common.constant.RedisConstant;
import com.atguigu.lease.common.jwt.JwtUtil;
import com.atguigu.lease.common.result.ResultCodeEnum;
import com.atguigu.lease.model.entity.SystemUser;
import com.atguigu.lease.model.entity.UserInfo;
import com.atguigu.lease.model.enums.BaseStatus;
import com.atguigu.lease.web.app.service.LoginService;
import com.atguigu.lease.web.app.service.UserInfoService;
import com.atguigu.lease.web.app.vo.user.LoginVo;
import com.atguigu.lease.web.app.vo.user.UserInfoVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import io.jsonwebtoken.Claims;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.cache.CacheProperties;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.Random;
import java.util.concurrent.TimeUnit;

@Service
public class LoginServiceImpl implements LoginService {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    @Autowired
    private UserInfoService userInfoService;

    /**
 * 重写获取验证码方法
 *
 * @param phone 用户手机号码，用于生成验证码和存储到缓存中
 *              此方法用于生成一个验证码，并将其存储到Redis缓存中，以供后续验证使用
 *              它首先检查缓存中是否已经存在未过期的验证码，如果存在，并且距离验证码过期时间不足重新发送间隔，
 *              则抛出异常提示用户发送验证码过于频繁如果不存在，或者存在但已过期，则生成新的验证码，
 *              并将其存储到缓存中
 */
@Override
public void getCode(String phone) {
    // 生成Redis中验证码的键
    String key = RedisConstant.APP_LOGIN_PREFIX + phone;
    // 检查Redis中是否存在该手机号对应的验证码
    Boolean b = redisTemplate.hasKey(key);
    if (b) {
        // 获取验证码的剩余过期时间
        Long expire = redisTemplate.getExpire(key, TimeUnit.SECONDS);
        // 获取验证码的总生存时间（秒）
        Integer appLoginCodeTtlSec = RedisConstant.APP_LOGIN_CODE_TTL_SEC;
        // 获取验证码重新发送的间隔时间（秒）
        Integer appLoginCodeResendTimeSec = RedisConstant.APP_LOGIN_CODE_RESEND_TIME_SEC;
        // 如果验证码剩余过期时间小于重新发送间隔时间，则认为发送验证码过于频繁
        if (appLoginCodeTtlSec - expire < appLoginCodeResendTimeSec) {
            throw new LeaseException(ResultCodeEnum.APP_SEND_SMS_TOO_OFTEN);
        }
    }
    // 生成随机验证码
    Random random = new Random();
    String code = random.nextInt(100000, 1000000) + "";
    // 打印验证码到控制台（实际应用中应发送到用户手机）
    System.out.println("验证码：" + code);
    // 将验证码存储到Redis中，并设置过期时间
    redisTemplate.opsForValue().set(key, code, RedisConstant.APP_LOGIN_CODE_TTL_SEC, TimeUnit.SECONDS);
}


    /**
 * 登录功能实现方法
 *
 * @param loginVo 登录视图对象，包含用户输入的手机号和验证码
 * @return 登录成功后的JWT令牌
 * @throws LeaseException 当验证码过期、错误或用户账户被禁用时抛出异常
 */
@Override
public String login(LoginVo loginVo) {
    // 构造Redis中验证码的键名
    String key = RedisConstant.APP_LOGIN_PREFIX + loginVo.getPhone();
    // 从Redis中获取验证码
    String code = (String) redisTemplate.opsForValue().get(key);
    // 验证码不存在或已过期
    if (code == null) {
        throw new LeaseException(ResultCodeEnum.APP_LOGIN_CODE_EXPIRED);
    }
    // 验证码不匹配
    if (!code.equals(loginVo.getCode())) {
        throw new LeaseException(ResultCodeEnum.APP_LOGIN_CODE_ERROR);
    }
    // 查询数据库中是否存在该用户
    LambdaQueryWrapper<UserInfo> queryWrapper = new LambdaQueryWrapper();
    queryWrapper.eq(UserInfo::getPhone, loginVo.getPhone());
    UserInfo userInfo = userInfoService.getOne(queryWrapper);
    // 用户不存在，则进行自动注册
    if (userInfo == null){
        userInfo = new UserInfo();
        userInfo.setPhone(loginVo.getPhone());
        // 设置默认头像
        userInfo.setAvatarUrl("https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif");
        // 设置默认昵称
        userInfo.setNickname("用户" + loginVo.getPhone().substring(7));
        userInfo.setStatus(BaseStatus.ENABLE);
        userInfoService.save(userInfo);
    }
    // 用户账户被禁用
    if(userInfo.getStatus() != BaseStatus.ENABLE){
        throw new LeaseException(ResultCodeEnum.APP_ACCOUNT_DISABLED_ERROR);
    }
    // 生成JWT令牌
    String token = JwtUtil.createToken(userInfo.getId(), userInfo.getNickname());
    return token;
}


    /**
 * 根据访问令牌获取自定义用户信息
 *
 * @param accessToken 访问令牌，用于验证用户身份
 * @return 返回用户信息对象，包含昵称和头像URL
 */
@Override
public UserInfoVo customInfo(String accessToken) {
    // 解析访问令牌，获取其中的用户信息声明
    Claims claims = JwtUtil.parseToken(accessToken);
    // 从声明中提取用户ID
    Integer userId = (Integer) claims.get("userId");
    // 根据用户ID获取用户信息实体
    UserInfo userInfo = userInfoService.getById(userId);
    // 创建用户信息视图对象，用于返回给客户端
    UserInfoVo userInfoVo = new UserInfoVo();
    // 设置用户昵称
    userInfoVo.setNickname(userInfo.getNickname());
    // 设置用户头像URL
    userInfoVo.setAvatarUrl(userInfo.getAvatarUrl());
    // 返回用户信息视图对象
    return userInfoVo;
}

}
